package com.ruoyi.cs.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.Response;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.CommonUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.common.utils.pay.FXPayConfig;
import com.ruoyi.common.utils.pay.HttpsUtils;
import com.ruoyi.common.utils.pay.SignUtils;
import com.ruoyi.common.utils.sign.Md5Utils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.cs.domain.TRecharge;
import com.ruoyi.cs.domain.TUser;
import com.ruoyi.cs.domain.vo.FXPayNotifyParam;
import com.ruoyi.cs.mapper.TRechargeMapper;
import com.ruoyi.cs.mapper.TUserMapper;
import com.ruoyi.cs.service.CommonService;
import com.ruoyi.cs.service.IFUXINPayService;
import com.ruoyi.cs.service.IJiuJiaPayService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.security.GeneralSecurityException;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
@SuppressWarnings("all")
public class JiuJiaPayServiceImpl implements IJiuJiaPayService {
    private Logger log = LoggerFactory.getLogger(JiuJiaPayServiceImpl.class);
    @Autowired
    private TUserMapper tUserMapper;
    @Autowired
    private CommonService commonService;
    @Autowired
    private TRechargeMapper tRechargeMapper;
    @Value("${ruoyi.jjiapayNotify}")
    private String cznotify;
    @Autowired
    private RedisCache redisCache;

    @Override
    public Response<Map> recharge(String userId, BigDecimal czMoney, String czEquipment) throws Exception {
        TUser tUser = tUserMapper.selectTUserById(userId);
        BigDecimal giveFee = new BigDecimal("0");
        try {
            giveFee = commonService.rechargeVerification(tUser, czMoney);
        } catch (Exception e) {
            return Response.fail(e.getMessage());
        }

        String out_trade_no = DateUtils.dateTimeNow() + IdUtils.fastUUID().substring(0, 8);
        BigDecimal fee = czMoney.multiply(new BigDecimal(6.5)).setScale(2, BigDecimal.ROUND_HALF_UP);
        Long starttime = System.currentTimeMillis();
        /**发送请求*/
        JSONObject jsonObject = this.jiuJiaPayment(fee.toString(), out_trade_no, cznotify);
        log.info("九嘉充值发送请求时间:{}", System.currentTimeMillis() - starttime);
        if (jsonObject == null) {
            return  Response.fail("发起支付失败");
        }
        String url = "";
        String data = "";
        if (jsonObject.getInteger("code") == 0) {
            url = jsonObject.getString("url");
            data = jsonObject.getString("data");
        } else {
            log.error("九嘉发起支付失败:{}", jsonObject.getString("msg"));
            return  Response.fail("支付响应失败");
        }
        String picurl = null;
        try {
            /**生产二维码图片*/
            picurl = commonService.scQrCodeImge(url);
        } catch (Exception e) {
            log.error("九嘉支付生产二维码订单异常:{}", e.getMessage());
            e.printStackTrace();
            return Response.fail("发送请求失败");
        }

        TRecharge tRecharge = new TRecharge();
        tRecharge.setUserId(userId);
        tRecharge.setCzId(out_trade_no);
        tRecharge.setDelFlag(Constants.SUCCESS);
        tRecharge.setCzType(Constants.ALIPAY);
        tRecharge.setPlatformOrderNo(data);
        tRecharge.setMerchantOrderNo(out_trade_no);
        /**支付结果  0 待充值 1充值成功 2充值失败*/
        tRecharge.setCzStatus(Constants.CZSTATUS0);
        /**交易类型*/
        tRecharge.setCzEquipment(czEquipment);
        tRecharge.setCreateTime(DateUtils.getNowDate());
        tRecharge.setGiveFee(giveFee);
        tRecharge.setCzMoney(czMoney);
        tRecharge.setCzPlatform(Constants.CZPLATFORM5);
        tRecharge.setUserType(tUser.getUserType());
        if (StringUtils.isNotEmpty(tUser.getUserPromotersId())) {
            tRecharge.setUserPromotersId(tUser.getUserPromotersId());
        }
        int count = tRechargeMapper.insertTRecharge(tRecharge);
        if (count > 0) {
            Map resultMap = new HashMap();
            resultMap.put("picUrl", picurl);
            resultMap.put("czId", out_trade_no);
            resultMap.put("totalFee", czMoney);
            resultMap.put("url", url);
            log.info("九嘉支付订单生产：参数信息：{}，支付金额：{},订单号：{}", JSON.toJSONString(tRecharge), fee, out_trade_no);
            return Response.success(resultMap);
        } else {
            log.error("九嘉支付充值记录表插入失败：参数信息：{}，支付金额：{},订单号：{}", JSON.toJSONString(tRecharge), fee, out_trade_no);
            return Response.fail("充值窗口异常，请联系管理员处理!");
        }
    }

    @Override
    public String notify(HttpServletRequest request) {
        Map<String, String> params = this.convertRequestParamsToMap(request);
        String msg = JSON.toJSONString(params);
        log.info("九嘉支付异步通知:{}", msg);
        try {
            String sign = params.get("sign");
            String md5 = notifySign(params, Constants.secret_jiujia);
            if (sign.equals(md5)) {
                String sdorderno = params.get("trade_no");//订单编号
                TRecharge tRecharge = tRechargeMapper.selectTRechargeById(sdorderno);
                String platform = tRecharge == null ? "" : tRecharge.getCzPlatform();
                log.info("九嘉支付回调签名认证成功");
                if (tRecharge != null) {
                    BigDecimal bg = tRecharge.getCzMoney();
                    BigDecimal fee = bg.multiply(new BigDecimal(6.5)).setScale(2, BigDecimal.ROUND_HALF_UP);
                    if (new BigDecimal(params.get("total_fee")).compareTo(new BigDecimal(fee.toString())) != 0) {
                        log.error("支付金额不对等,请求参数:{}",msg);
                        return "success";
                    }
                    String result_code = params.get("result_code");
                    String out_trade_no = params.get("out_trade_no");
                    if(Constants.CZSTATUS0.equals(tRecharge.getCzStatus())){
                        if ("success".equals(result_code)) {
                            commonService.czSuccessHandle(tRecharge,sdorderno,out_trade_no,msg);
                        } else if ("fail".equals(result_code)) {
                            /**充值失败处理*/
                            commonService.czFailHandle(tRecharge,sdorderno,out_trade_no,msg);
                        } else {
                            log.error("没有处理回调业务，交易状态：{},params:{}", result_code, msg);
                        }
                    }else {
                        log.error("没有处理回调业务，交易状态：{},params:{}", result_code, msg);
                    }
                    return "success";
                } else {
                    log.error("查询充值订单信息为空,请求参数：{}", msg);
                    return "fail";
                }
            } else {
                log.info("富信回调签名认证失败，signVerified=false, paramsJson:{}", msg);
                return "fail";
            }
        } catch (Exception e) {
            log.error("回调异常,paramsJson:{},errorMsg:{}", msg, e.getMessage());
            return "fail";
        }
    }
    public Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
        Map<String, String> retMap = new HashMap<String, String>();
        Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;
            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) {
                    sb.append(",").append(val);
                }
                retMap.put(name, sb.toString().substring(1));
            } else {
                retMap.put(name, "");
            }
        }
        return retMap;
    }

    public JSONObject jiuJiaPayment(String rechargefee, String orderId, String callback_url) {
        String ip = IpUtils.getIpAddr(ServletUtils.getRequest());
        BigDecimal feefloat = new BigDecimal(rechargefee).setScale(2);
        Map<String, String> payData = new HashMap<>();
        payData.put("app_key", Constants.app_key);
        payData.put("api_domain", "https://b5skins.com");
        payData.put("subject", "B5电商平台");
        payData.put("pay_type", "3");
        payData.put("goods_id", "123456");
        payData.put("goods_price", feefloat.toString());
        payData.put("goods_num", "1");
        payData.put("order_id", orderId);
        payData.put("total_amount", feefloat.toString());
        payData.put("member_id", Constants.member_id);
        payData.put("callback_url", callback_url);
        payData.put("sign", signKY(payData, Constants.secret_jiujia));
        payData.put("user_ip",ip);
        System.out.println("支付参数:" + JSON.toJSONString(payData));
        String result = HttpsUtils.http("https://zhifu.jiujiaka.com/alipay/create_pay2", payData);
        JSONObject jsonObject = JSON.parseObject(result);
        return jsonObject;
    }

    //支付签名算法
    public static String signKY(Map<String, String> params, String secret) {
        String sign = "";
        StringBuilder sb = new StringBuilder();
        sb.append("member_id").append("=").append(params.get("member_id")).append("&")
                .append("app_key").append("=").append(params.get("app_key")).append("&")
                .append("api_domain").append("=").append(params.get("api_domain")).append("&")
                .append("total_amount").append("=").append(params.get("total_amount")).append("&")
                .append("callback_url").append("=").append(params.get("callback_url")).append("&")
                .append("order_id").append("=").append(params.get("order_id")).append("&");
        sb.append("key=").append(secret);
        System.out.println("sign内容:" + sb.toString());
        byte[] md5Digest;
        try {
            //得到Md5加密得到sign
            md5Digest = getMD5Digest(sb.toString());
            sign = byte2hex(md5Digest).toLowerCase();
        } catch (IOException e) {
            System.out.println("生成签名错误" + e);
        }
        System.out.println("签名串：" + sign);
        return sign;
    }

    private static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }

    private static byte[] getMD5Digest(String data) throws IOException {
        byte[] bytes = null;
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            bytes = md.digest(data.getBytes("UTF-8"));
        } catch (GeneralSecurityException gse) {
            throw new IOException(gse);
        }
        return bytes;
    }

    /**
     * 异步回调签名算法
     */
    public static String notifySign(Map<String, String> params, String secret) {
        String sign = "";
        StringBuilder sb = new StringBuilder();
        sb.append("member_id=");
        sb.append(params.get("member_id"));
        sb.append("&");
        sb.append("total_fee=");
        sb.append(params.get("total_fee"));
        sb.append("&");
        sb.append("result_code=");
        sb.append(params.get("result_code"));
        sb.append("&");
        sb.append("trade_no=");
        sb.append(params.get("trade_no"));
        sb.append("&");
        sb.append("out_trade_no=");
        sb.append(params.get("out_trade_no"));
        sb.append("&");
        sb.append("key=");
        sb.append(secret);
        sign = Md5Utils.hash(sb.toString()).toLowerCase();
        return sign;
    }

}
